Skip to content

fix(spv): add distinct error state for SPV sync failures#650

Open
lklimek wants to merge 8 commits intov1.0-devfrom
fix/spv-sync-error-status
Open

fix(spv): add distinct error state for SPV sync failures#650
lklimek wants to merge 8 commits intov1.0-devfrom
fix/spv-sync-error-status

Conversation

@lklimek
Copy link
Contributor

@lklimek lklimek commented Feb 24, 2026

Issue being fixed or feature implemented

SPV sync gets permanently stuck in "Syncing" status when a fatal error occurs
(e.g., masternode QRInfo failure). The connectivity icon stays orange instead
of turning red, and no error message is shown to the user.

Symptoms observed in logs:

ERROR dash_spv::sync::masternodes::sync_manager: QRInfo failed after 4 retries: Required rotated chain lock sig at h - 0 not present for masternode diff block hash: ...
ERROR dash_spv::sync::sync_manager: Masternode error handling message: Masternode sync failed: ...

Despite these errors, the UI showed no indication of failure.

Root cause: Two gaps in error detection:

  1. spawn_sync_event_handler ignored SyncEvent::ManagerError events entirely.
  2. spawn_progress_watcher only checked is_synced() and defaulted everything
    else to SpvStatus::Syncing, never checking for SyncState::Error.

Additionally, an upstream bug in dash-spv (dashpay/rust-dashcore#469) means
the progress channel never receives the error state update, making gap #2
a dead path until the library is fixed.

What was done?

Error detection (manager.rs)

Added two error detection paths in src/spv/manager.rs:

  1. spawn_sync_event_handler (primary path): Now handles
    SyncEvent::ManagerError — transitions SpvStatus to Error and stores
    the error message in last_error. This works reliably because dash-spv
    always emits this event on manager failure.

  2. spawn_progress_watcher (defense-in-depth): Now checks
    watch_progress.state() == SyncState::Error and transitions accordingly.
    Currently blocked by upstream bug but will activate once
    bug: SyncManager does not emit progress update on manager error path rust-dashcore#469 is fixed.

Distinct Error UI state (connection_status.rs, top_panel.rs, theme.rs)

Previously, SpvStatus::Error mapped to OverallConnectionState::Disconnected
the same red static circle as "never connected". Users couldn't distinguish
between the two. Now:

  • Added OverallConnectionState::Error variant (repr 3)
  • SpvStatus::Error maps to OverallConnectionState::Error explicitly
  • Visual treatment: magenta circle with slow alarm pulse + white "!" glyph
  • Tooltip: shows "SPV sync error: {detail}" with the specific error message
    from spv_last_error (populated from SpvManager::status().last_error)
  • Added DashColors::sync_error_color() for the magenta-red indicator color
  • RPC mode includes a forward-compatible Error arm ("Connection error")

Related upstream issues

How has this been tested?

Breaking Changes

None

🤖 Co-authored by Claudius the Magnificent AI Agent

Summary by CodeRabbit

  • New Features

    • Connection indicator gains a distinct SPV sync error state: magenta color, centered "!" glyph, and matching pulsation/repaint behavior.
    • Tooltips show a "Connection error" header and, when available, an "SPV sync error: " line with captured error details.
    • SPV error details are preserved and surfaced in the UI until cleared.
  • Documentation

    • Added manual test scenarios for SPV sync error verification, icon transitions (syncing/running/error), error message content, and log guidance.

The SpvManager's progress watcher and sync event handler did not detect
fatal sync errors, leaving the UI stuck in "Syncing" status indefinitely.

Two detection paths are now in place:

1. spawn_sync_event_handler handles SyncEvent::ManagerError to transition
   SpvStatus to Error and store the error message. This is the primary
   path since dash-spv always emits this event on manager failure.

2. spawn_progress_watcher checks SyncState::Error from the progress
   channel as defense-in-depth (currently blocked by upstream bug
   dashpay/rust-dashcore#469 where try_emit_progress is not called on
   error paths).

Once SpvStatus::Error is set, the existing ConnectionStatus logic maps
it to OverallConnectionState::Disconnected, showing a red indicator icon
with "SPV: Error" in the tooltip.

Related upstream issues:
- dashpay/rust-dashcore#469 (missing progress emit on error)
- dashpay/rust-dashcore#470 (QRInfo chain lock retry robustness)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Propagates SPV sync errors from the SPV manager into connection status and UI, introduces OverallConnectionState::Error and spv_last_error storage, updates top-panel indicator and theme color, and adds a manual test scenarios document for verifying SPV error display and logs.

Changes

Cohort / File(s) Summary
SPV Manager
src/spv/manager.rs
Detects SyncState::Error in progress watcher, provides failed_manager_name, stores last_error (Arc) without clobbering, handles SyncEvent::ManagerError, sets status to Error and logs detailed messages.
Connection Status / State
src/context/connection_status.rs
Adds OverallConnectionState::Error, spv_last_error: Mutex<Option<String>>; maps SpvStatus::ErrorOverallConnectionState::Error, persists/clears spv_last_error during refresh/reset, and surfaces SPV error detail in tooltip_text().
UI Indicator
src/ui/components/top_panel.rs
Adds Error rendering: uses sync_error_color, includes Error pulsation logic, repaints for Error, and draws a centered white "!" glyph on the indicator.
Theme Colors
src/ui/theme.rs
Adds DashColors::sync_error_color(dark_mode: bool) returning magenta tones for error state.
Documentation
docs/ai-design/.../manual-test-scenarios.md
New manual test scenarios covering SPV sync error reproduction, expected icon color/state transitions, tooltip texts, error message contents, and where to find diagnostic logs.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(200,200,255,0.5)
    participant SPV as SPV Library
    end
    rect rgba(200,255,200,0.5)
    participant Manager as SPV Manager
    end
    rect rgba(255,200,200,0.5)
    participant Status as ConnectionStatus
    end
    rect rgba(255,230,200,0.5)
    participant UI as TopPanel/UI
    end

    SPV->>Manager: progress update (SyncState)
    alt SyncState::Error
        Manager->>Manager: set last_error (Arc) if not present
        Manager->>Status: update SpvStatus::Error with detail
        Manager->>Status: emit diagnostic log
        Status->>UI: refresh -> OverallConnectionState::Error (include detail)
        UI->>UI: render magenta indicator + "!"
    else SyncState::Running/Complete
        Manager->>Status: update SpvStatus::Running/Synced
        Status->>UI: refresh -> Synced/Syncing
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I twitched my whiskers at magenta light,
A tiny "!" beamed bold into the night.
Manager whispered what the logs would show,
Tooltips now tell where the errors go.
Hop — the bug's name now has a glow!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(spv): add distinct error state for SPV sync failures' directly summarizes the main change: implementing a new error state for SPV sync failures.
Docstring Coverage ✅ Passed Docstring coverage is 91.67% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/spv-sync-error-status

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/spv/manager.rs`:
- Around line 1082-1087: The progress-watcher branch currently unconditionally
sets last_error to "Sync failed (reported by SPV library)" when is_error is
true, which can overwrite a more detailed error previously set by the sync event
handler; change the logic in the is_error branch (where SpvStatus::Error is
assigned) to open last_error.write(), inspect whether it is None, and only
assign the generic message if err_guard.is_none(), leaving an existing detailed
message intact (keep the existing status_guard = SpvStatus::Error assignment).

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 79e1479 and e409c29.

📒 Files selected for processing (2)
  • docs/ai-design/2026-02-24-spv-sync-error-status/manual-test-scenarios.md
  • src/spv/manager.rs

Only set the generic "Sync failed" message in last_error when no
detailed message has already been stored by the sync event handler.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/spv/manager.rs (1)

1082-1089: Past concern resolved — is_none() guard correctly preserves detailed error message.

The err_guard.is_none() check ensures the generic fallback message does not overwrite a detailed message already stored by the sync event handler, which is the fix requested in the prior review.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/spv/manager.rs` around lines 1082 - 1089, The code correctly avoids
overwriting a detailed error by checking last_error.write() and
err_guard.is_none() before setting a generic message; no code change
needed—ensure the conditional around SpvStatus::Error uses the same pattern
(is_error branch setting *status_guard = SpvStatus::Error and only writing to
last_error when err_guard.is_none()) to preserve previously stored detailed
errors from the sync event handler.
🧹 Nitpick comments (1)
src/spv/manager.rs (1)

1082-1089: Consider adding inline unit tests for the new error-transition paths.

Neither the is_error branch in spawn_progress_watcher nor the SyncEvent::ManagerError handler in spawn_sync_event_handler has test coverage. The status/last_error mutation logic can be tested independently of the async spawn by extracting it into a small helper or using tokio::test. As per coding guidelines, unit tests should be written inline using #[test].

Also applies to: 1155-1163

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/spv/manager.rs` around lines 1082 - 1089, Add inline unit tests covering
the error-transition logic by extracting the status/last_error mutation into a
small helper (e.g., set_spv_error_status or update_status_on_error) and then
write #[test] (or #[tokio::test] if async) cases that exercise the is_error
branch used by spawn_progress_watcher and the SyncEvent::ManagerError handling
used by spawn_sync_event_handler; tests should call the helper (or invoke the
handler functions directly) to assert SpvStatus becomes SpvStatus::Error and
last_error is set to Some(...) when an error condition is simulated, and also
include a case where last_error is already Some to ensure it is not overwritten.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/spv/manager.rs`:
- Around line 1082-1089: The code correctly avoids overwriting a detailed error
by checking last_error.write() and err_guard.is_none() before setting a generic
message; no code change needed—ensure the conditional around SpvStatus::Error
uses the same pattern (is_error branch setting *status_guard = SpvStatus::Error
and only writing to last_error when err_guard.is_none()) to preserve previously
stored detailed errors from the sync event handler.

---

Nitpick comments:
In `@src/spv/manager.rs`:
- Around line 1082-1089: Add inline unit tests covering the error-transition
logic by extracting the status/last_error mutation into a small helper (e.g.,
set_spv_error_status or update_status_on_error) and then write #[test] (or
#[tokio::test] if async) cases that exercise the is_error branch used by
spawn_progress_watcher and the SyncEvent::ManagerError handling used by
spawn_sync_event_handler; tests should call the helper (or invoke the handler
functions directly) to assert SpvStatus becomes SpvStatus::Error and last_error
is set to Some(...) when an error condition is simulated, and also include a
case where last_error is already Some to ensure it is not overwritten.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e409c29 and 3b6e3a2.

📒 Files selected for processing (1)
  • src/spv/manager.rs

Distinguish "connected but sync failed" from "never connected" by
adding an `OverallConnectionState::Error` variant. SPV sync errors
now show a magenta pulsating circle with "!" glyph instead of the
same red static circle used for disconnected state. Tooltip shows
the specific SPV error message for easier debugging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@lklimek lklimek changed the title fix(spv): transition to error status when sync manager fails fix(spv): add distinct error state for SPV sync failures Feb 24, 2026
- Move last_error write outside status write guard in
  spawn_progress_watcher to maintain consistent lock ordering
  (status → release → last_error), eliminating latent deadlock risk.
- Update manual test scenarios to match actual implementation:
  magenta pulsating "!" indicator (not red/static/Disconnected).
- Add Scenario 4 to explicitly verify Error vs Disconnected distinction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@lklimek
Copy link
Contributor Author

lklimek commented Feb 24, 2026

Audit Summary

Reviewed by: Claude Code with a 2-agent team:

  • code-reviewer — correctness, conventions, state machine, UI logic, test doc accuracy
  • security-engineer — concurrency safety, OWASP classification, information disclosure, resource consumption

Overall Risk: LOW — well-scoped status propagation change with no new attack surface.

Findings

# Severity OWASP Location Description Status
1 MEDIUM manual-test-scenarios.md Test doc described old behavior (red/static/Disconnected) instead of new (magenta/pulsing/"SPV sync error") ✅ Fixed in c7f66d4
2 MEDIUM A04 manager.rs:1079-1089 Nested lock: last_error write acquired inside status write guard — inconsistent ordering with event handler path (CWE-833) ✅ Fixed in c7f66d4
3 MEDIUM A04 connection_status.rs:227-278 TOCTOU: tooltip_text() reads overall_state() then spv_last_error separately — state can change between reads (CWE-367). Impact cosmetic only, corrects on next frame. ℹ️ Accepted
4 LOW A09 manager.rs:1161 Error string from dash-spv displayed verbatim in tooltip — could expose peer IPs/block hashes during screen sharing (CWE-209). Desktop app, local user already has log access. ℹ️ Note
5 LOW top_panel.rs:123,166 Error pulsation runs indefinitely since Error is a terminal state — continuous repaints (CWE-400). Minor CPU/GPU concern for long sessions. ℹ️ Note
6 LOW connection_status.rs:350 Error state polls every 2s (same as Disconnected). Since Error is terminal without user action, longer interval would reduce wasted work. ℹ️ Note
7 LOW connection_status.rs:252 RPC-mode Error tooltip reads generic "Connection error" — currently unreachable (RPC never produces Error). Forward-compat placeholder, harmless. ℹ️ Note
8 LOW connection_status.rs:53 spv_last_error uses Mutex while SpvManager.last_error uses RwLock — stylistic inconsistency, no bug. ℹ️ Note

Pre-existing / Out-of-scope

  • Existing CodeRabbit review comment about last_error overwrite was already addressed by the is_none() guard + nested lock fix in c7f66d4.

Positive Observations

  • All match arms on OverallConnectionState are exhaustive across the codebase — no missed patterns.
  • network_chooser_screen.rs != Disconnected checks naturally and correctly include Error.
  • Excellent explanatory comment in event handler about why direct ManagerError handling is needed (dash-spv doesn't update progress channel on error path).
  • reset() properly clears spv_last_error — good state hygiene.
  • Clean state machine: Error reachable only via explicit SpvStatus::Error, cleared on start()/reset().
  • Consistent poisoned-lock handling (if let Ok(...)) throughout.
  • Commit structure is logical and atomic across 4 commits.

Redundancy

Both agents flagged the nested lock issue (code-reviewer as "overwrite asymmetry", security as "nested lock acquisition"). Security's framing was more precise — the lock ordering is the root concern. Redundancy ratio: 1 finding out of 8 unique = 12.5%.

Verdict: Approve. The two MEDIUM findings have been fixed. Remaining LOW/INFO items are acceptable trade-offs for a desktop application.

🤖 Co-authored by Claudius the Magnificent AI Agent

…r-status

# Conflicts:
#	src/context/connection_status.rs
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/ui/components/top_panel.rs (1)

117-125: Note: Error state pulsation runs indefinitely.

The pulsation continues for the terminal Error state since it doesn't match Disconnected. This causes continuous repaints via repaint_animation() at line 167. Per PR audit this is noted as a LOW-priority concern (minor CPU/GPU usage).

If desired, you could stop the pulsation for Error state by adding it to the non-pulsating check:

OverallConnectionState::Disconnected | OverallConnectionState::Error => 1.0,

But this is optional since the visual feedback of a pulsating error indicator may be intentional UX.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/components/top_panel.rs` around lines 117 - 125, The Error state is
currently treated as pulsating because pulse_scale's match covers
OverallConnectionState::Error with a sinusoidal value, causing continuous
repaints via repaint_animation(); to stop pulsation for terminal Error state
(like Disconnected) change the match arm for pulse_scale so Error maps to the
constant 1.0 instead of a sine expression (update the match in the code that
computes pulse_scale for OverallConnectionState), ensuring repaint_animation()
no longer receives continuous animation-triggering updates when state is Error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/ui/components/top_panel.rs`:
- Around line 117-125: The Error state is currently treated as pulsating because
pulse_scale's match covers OverallConnectionState::Error with a sinusoidal
value, causing continuous repaints via repaint_animation(); to stop pulsation
for terminal Error state (like Disconnected) change the match arm for
pulse_scale so Error maps to the constant 1.0 instead of a sine expression
(update the match in the code that computes pulse_scale for
OverallConnectionState), ensuring repaint_animation() no longer receives
continuous animation-triggering updates when state is Error.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7f66d4 and 61e9bde.

📒 Files selected for processing (2)
  • src/context/connection_status.rs
  • src/ui/components/top_panel.rs

@lklimek
Copy link
Contributor Author

lklimek commented Feb 26, 2026

🔍 Audit Summary — PR #650

Reviewed by: Claude Code with a 2-agent team:

  • security-engineer (opus) — concurrency safety, error handling, CWE/OWASP classification, dependency research
  • rust-developer (sonnet) — idiomatic Rust, lock patterns, match exhaustiveness, clippy concerns

Overall Assessment

Clean, well-scoped PR that fills a real UX gap. No critical or high-severity issues. The concurrency model is sound, match arms are exhaustive, and the error state lifecycle (create → display → cleanup) is handled correctly across all three touchpoints (manager, connection status, UI).

Findings

# Severity Location Description OWASP/CWE
1 MEDIUM manager.rs:1163-1164 Unbounded error string from dash-spv stored verbatim — truncate to ~512 chars CWE-400
2 MEDIUM manager.rs:1158-1166 Lock ordering correct but implicit — add explicit drop(guard) or comment CWE-667
3 MEDIUM manager.rs:1090 vs 1163 Inconsistent last_error write policy — progress watcher is first-error-wins, event handler is last-wins
4 LOW connection_status.rs:270-281 Unnecessary .to_string() allocations in non-error match arms
5 LOW manager.rs:1094-1095 Generic fallback error message has no detail from SpvSyncProgress
6 LOW connection_status.rs:54 Mutex vs RwLock inconsistency for spv_last_error
7 LOW top_panel.rs:165 Comment says "only when not disconnected" — doesn't mention Error is also animated

Pre-existing / Out-of-scope

  • Silent lock poisoning (.lock().ok() / .write().ok() pattern): Used throughout the codebase. Not introduced by this PR, but new instances are added. Consider logging warnings on poisoned locks in a future cleanup.

Positive Observations

  • ✅ Lock ordering documented in progress watcher (status → release → last_error)
  • ✅ Error state properly cleared on start/stop/reset (3 cleanup paths)
  • ✅ Let-chains are stable under edition 2024 — no nightly features
  • ✅ All match on OverallConnectionState are exhaustive (including network_chooser_screen.rs)
  • ✅ No dependency vulnerabilities found (dash-spv, egui 0.33, std sync primitives)

🤖 Reviewed by Claudius the Magnificent AI Agent

…llocations

- Add explicit drop(guard) in spawn_sync_event_handler for lock ordering
- Use first-error-wins policy consistently, log subsequent errors
- Add failed_manager_name() helper for detailed progress-channel errors
- Add TODO for error string truncation (CWE-400)
- Use Cow<str> in SPV tooltip to avoid unnecessary allocations
- Document Mutex choice for spv_last_error
- Update animation repaint comment to list all pulsating states

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@lklimek
Copy link
Contributor Author

lklimek commented Feb 26, 2026

🔍 Re-Audit Summary — PR #650 (Round 2)

Reviewed by: Claude Code with a 2-agent team:

  • security-engineer (opus) — concurrency, CWE/OWASP, dependency research
  • rust-developer (sonnet) — idiomatic Rust, code quality, UI correctness

Overall Assessment

All 7 findings from the first review are properly addressed. Security engineer approves for merge. One new MEDIUM found by the Rust reviewer (tooltip inconsistency), plus minor items.

New Findings

# Severity Location Description
1 MEDIUM connection_status.rs:285-295 Misleading tooltip: Error state falls through to spv_phase_summary() which only matches Syncing phases → shows "SPV: syncing..." under an error header
2 LOW manager.rs:1190 {:?} (Debug) vs {} (Display) for ManagerIdentifier — inconsistent between tracing and error message
3 LOW manager.rs:1195-1196 TODO for error string truncation (CWE-400) — track as follow-up issue
4 LOW manager.rs:1048-1066 failed_manager_name() checks masternodes first (most common failure) but order differs from pipeline order in spv_phase_summary() — add comment explaining why
5 LOW manager.rs:1202 tracing::warn logs full error string — low risk for desktop app, note if log shipping added

Finding #1 Detail

When SpvStatus::Error, the spv_label falls through to the else branch which calls spv_phase_summary(). That function only matches SyncState::Syncing phases — in an error state, no phase is syncing, so it returns the fallback "syncing...". The tooltip would read:

SPV sync error: Sync manager Masternode failed: ...
SPV: syncing...                    ← contradicts the header
DAPI: Available (3/5 endpoints)

Fix: Add an explicit branch for SpvStatus::Error returning "SPV: Error".

Verification of Previous Fixes

Original # Finding Status
1 Unbounded error string ✅ TODO added (acceptable for follow-up)
2 Implicit lock ordering ✅ Explicit drop(guard) with comment
3 Inconsistent write semantics ✅ First-error-wins everywhere, subsequent logged
4 Unnecessary .to_string() ✅ Clean Cow<str>
5 Generic fallback error failed_manager_name() extracts phase detail
6 Mutex vs RwLock ✅ Documented with rationale comment
7 Stale animation comment ✅ Lists all pulsating states

Positive Observations

  • ✅ Lock ordering discipline consistent across both handlers
  • Cow<str> usage is clean and idiomatic
  • failed_manager_name() is sound with is_ok_and() guards
  • ✅ Mutex rationale comment prevents future "just use RwLock" instinct
  • ✅ No dependency vulnerabilities found

Redundancy ratio: 0/5 new findings overlapped between agents (disjoint focus areas this round).

🤖 Reviewed by Claudius the Magnificent AI Agent

lklimek and others added 2 commits February 26, 2026 14:17
- Add explicit SpvStatus::Error branch in spv_label to show "SPV: Error"
  instead of falling through to spv_phase_summary() which shows "syncing..."
- Use Display ({}) instead of Debug ({:?}) for ManagerIdentifier in tracing
- Document masternodes-first order in failed_manager_name()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@lklimek lklimek added the human review This PR is ready for human review. label Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human review This PR is ready for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant